(function($) {
    var $scrollTo = $.scrollTo = function(target, duration, settings) {
        $(window).scrollTo(target, duration, settings);
    };
    $scrollTo.defaults = {
        axis: 'y',
        duration: 1
    };
    $scrollTo.window = function(scope) {
        return $(window).scrollable();
    };
    $.fn.scrollable = function() {
        return this.map(function() {
            var win = this.parentWindow || this.defaultView,
            elem = this.nodeName == '#document' ? win.frameElement || win: this,
            doc = elem.contentDocument || (elem.contentWindow || elem).document,
            isWin = elem.setInterval;
            return elem.nodeName == 'IFRAME' || isWin && $.browser.safari ? doc.body: isWin ? doc.documentElement: this;
        });
    };
    $.fn.scrollTo = function(target, duration, settings) {
        if (typeof duration == 'object') {
            settings = duration;
            duration = 0;
        }
        if (typeof settings == 'function') settings = {
            onAfter: settings
        };
        settings = $.extend({},
        $scrollTo.defaults, settings);
        duration = duration || settings.speed || settings.duration;
        settings.queue = settings.queue && settings.axis.length > 1;
        if (settings.queue) duration /= 2;
        settings.offset = both(settings.offset);
        settings.over = both(settings.over);
        return this.scrollable().each(function() {
            var elem = this,
            $elem = $(elem),
            targ = target,
            toff,
            attr = {},
            win = $elem.is('html,body');
            switch (typeof targ) {
            case 'number':
            case 'string':
                if (/^([+-]=)?\d+(px)?$/.test(targ)) {
                    targ = both(targ);
                    break;
                }
                targ = $(targ, this);
            case 'object':
                if (targ.is || targ.style) toff = (targ = $(targ)).offset();
            }
            $.each(settings.axis.split(''),
            function(i, axis) {
                var Pos = axis == 'x' ? 'Left': 'Top',
                pos = Pos.toLowerCase(),
                key = 'scroll' + Pos,
                old = elem[key],
                Dim = axis == 'x' ? 'Width': 'Height',
                dim = Dim.toLowerCase();
                if (toff) {
                    attr[key] = toff[pos] + (win ? 0 : old - $elem.offset()[pos]);
                    if (settings.margin) {
                        attr[key] -= parseInt(targ.css('margin' + Pos)) || 0;
                        attr[key] -= parseInt(targ.css('border' + Pos + 'Width')) || 0;
                    }
                    attr[key] += settings.offset[pos] || 0;
                    if (settings.over[pos]) attr[key] += targ[dim]() * settings.over[pos];
                } else attr[key] = targ[pos];
                if (/^\d+$/.test(attr[key])) attr[key] = attr[key] <= 0 ? 0 : Math.min(attr[key], max(Dim));
                if (!i && settings.queue) {
                    if (old != attr[key]) animate(settings.onAfterFirst);
                    delete attr[key];
                }
            });
            animate(settings.onAfter);
            function animate(callback) {
                $elem.animate(attr, duration, settings.easing, callback &&
                function() {
                    callback.call(this, target, settings);
                });
            };
            function max(Dim) {
                var attr = 'scroll' + Dim,
                doc = elem.ownerDocument;
                return win ? Math.max(doc.documentElement[attr], doc.body[attr]) : elem[attr];
            };
        }).end();
    };
    function both(val) {
        return typeof val == 'object' ? val: {
            top: val,
            left: val
        };
    };
})(jQuery);